#include <common.h>
#include <environment.h>
#include <asm/arch/hardware.h>
#include <asm/arch/platform.h>

#define FH_CHIP_FH8856          0x885600A1
#define FH_CHIP_FH8852          0x885600B1
#define FH_CHIP_FH8626V100      0x8626A100
#define FH_CHIP_FH8852V200      0x8852A200
#define FH_CHIP_FH8856V200      0x8856A200
#define FH_CHIP_FH8858V200      0x8858A200
#define FH_CHIP_FH8652          0x8652A100
#define FH_CHIP_FH8656          0x8656A100
#define FH_CHIP_FH8658          0x8658A100
#define FH_CHIP_FH8898          0x8898A100
#define FH_CHIP_FH8896          0x8896A100
#define FH_CHIP_FH8210          0x8210A100
#define FH_CHIP_FH8897          0x8897A100
#define FH_CHIP_FH8856V300      0x8856A300
#define FH_CHIP_FH8856V310      0x8856A310
#define FH_CHIP_FH8852V310      0x8852A310
#define FH_CHIP_FH8858V300      0x8858A300
#define FH_CHIP_FH8858V310      0x8858A310
#define FH_CHIP_FH8862          0x8862A100
#define FH_CHIP_FH8866          0x8866A100
#define FH_CHIP_MC6322          0x6322A100
#define FH_CHIP_MC6326          0x6326A100
#define FH_CHIP_MC6322L         0x6322B100
#define FH_CHIP_MC6326L         0x6326B100
#define FH_CHIP_MC6321          0x6321A100
#define FH_CHIP_MC6321L         0x6321B100
#define FH_CHIP_MC3303          0x3303A100


struct fh_chip_info
{
    unsigned int _plat_id; /* 芯片寄存器中的plat_id */
    unsigned int _chip_id; /* 芯片寄存器中的chip_id */
    unsigned int _chip_mask; /* 芯片寄存器中的chip_id */
    unsigned int chip_id; /* 芯片chip_id，详见上述定义 */
    unsigned int ddr_size; /* 芯片DDR大小，单位Mbit */
    char chip_name[32]; /* 芯片名称 */
};

#define CHIP_INFO(__plat_id, __chip_id, __chip_mask, chip, size) \
	{ \
		._plat_id = __plat_id, \
		._chip_id = __chip_id, \
		._chip_mask = __chip_mask, \
		.chip_id = FH_CHIP_##chip, \
		.ddr_size = size, \
		.chip_name = #chip, \
	},

#define RD_REG		0xffffffff

static struct fh_chip_info chip_infos[] = {
	CHIP_INFO(0x17092901, 0xC, 0xF, FH8852, 512)
	CHIP_INFO(0x17092901, 0xD, 0xF, FH8856, 1024)
	CHIP_INFO(0x18112301, 0x0, 0x0, FH8626V100, 512)
	CHIP_INFO(0x19112201, 0xc, 0xc, FH8852V200, RD_REG)
	CHIP_INFO(0x19112201, 0x4, 0xc, FH8858V200, RD_REG)
	CHIP_INFO(0x19112201, 0x8, 0xc, FH8856V200, RD_REG)
        CHIP_INFO(0x20031601, 0x00410005, 0x00ffffff, FH8858V300, RD_REG)
        CHIP_INFO(0x22071801, 0x00410007, 0x00ffffff, FH8858V310, RD_REG)
        CHIP_INFO(0x20031601, 0x00100005, 0x00ffffff, FH8856V300, RD_REG)
        CHIP_INFO(0x22071801, 0x00100007, 0x00ffffff, FH8856V310, RD_REG)
        CHIP_INFO(0x22071801, 0x00000007, 0x00ffffff, FH8852V310, RD_REG)
	CHIP_INFO(0x20031601, 0xc, 0xc, FH8652, RD_REG)
	CHIP_INFO(0x20031601, 0x8, 0xc, FH8656, RD_REG)
	CHIP_INFO(0x20031601, 0x4, 0xc, FH8658, RD_REG)
	CHIP_INFO(0x20122801, 0x00600004, 0x00FFFFFF, FH8898, RD_REG)
	CHIP_INFO(0x20122801, 0x00610004, 0x00FFFFFF, FH8896, RD_REG)
	CHIP_INFO(0x20122801, 0x00120004, 0x00FFFFFF, FH8210, RD_REG)
	CHIP_INFO(0x20122801, 0x00630004, 0x00FFFFFF, FH8897, RD_REG)
	CHIP_INFO(0x21101901, 0x00400006, 0x00FFFFFF, FH8862, RD_REG)
	CHIP_INFO(0x22081701, 0x00400008, 0x00FFFFFF, FH8866, RD_REG)
	CHIP_INFO(0x20122801, 0x0000000a, 0x00FFFFFF, MC6322, RD_REG)
	CHIP_INFO(0x20122801, 0x0001000a, 0x00FFFFFF, MC6322L, RD_REG)
	CHIP_INFO(0x20122801, 0x0002000a, 0x00FFFFFF, MC6326L, RD_REG)
	CHIP_INFO(0x20122801, 0x0003000a, 0x00FFFFFF, MC6326, RD_REG)
	CHIP_INFO(0x20122801, 0x0000020a, 0x00FFFFFF, MC6322, RD_REG)
	CHIP_INFO(0x20122801, 0x0000170a, 0x00FFFFFF, MC6321, RD_REG)
	CHIP_INFO(0x20122801, 0x0000140a, 0x00FFFFFF, MC6321L, RD_REG)
	CHIP_INFO(0x20122801, 0x0000060a, 0x00FFFFFF, MC3303, RD_REG)
};

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif

void fh_get_chipid(unsigned int *plat_id, unsigned int *chip_id)
{
	unsigned int _plat_id = 0;

	_plat_id = GET_REG(REG_PMU_CHIP_ID);
	if (plat_id != NULL)
		*plat_id = _plat_id;

	if (chip_id != NULL)
#ifdef REG_PMU_IP_VER
		*chip_id = GET_REG(REG_PMU_IP_VER);
#else
		*chip_id = 0;
#endif
}

struct fh_chip_info *fh_get_chip_info(void)
{
	static struct fh_chip_info *chip_info = NULL;
	struct fh_chip_info *info = NULL;
	unsigned int plat_id = 0;
	unsigned int chip_id = 0;
	int i = 0;

	if (chip_info != NULL)
		return chip_info;

	fh_get_chipid(&plat_id, &chip_id);
#ifdef REG_PMU_CHIP_INFO
	chip_id = GET_REG(REG_PMU_CHIP_INFO);
#endif
	for (i = 0; i < ARRAY_SIZE(chip_infos); i++)
	{
		info = &chip_infos[i];
		if (plat_id == info->_plat_id && (chip_id & info->_chip_mask) == info->_chip_id)
		{
			chip_info = info;
#ifdef REG_PMU_DDR_SIZE
			if (chip_info->ddr_size == RD_REG)
				chip_info->ddr_size = GET_REG(REG_PMU_DDR_SIZE);
#endif
			return info;
		}
	}

	return NULL;
}

unsigned int fh_get_ddrsize_mbit(void)
{
	struct fh_chip_info *info = fh_get_chip_info();

	if (info)
		return info->ddr_size;
	return 0;
}

char *fh_get_chipname(void)
{
	struct fh_chip_info *info = fh_get_chip_info();

	if (info)
		return info->chip_name;
	return "UNKNOWN";
}

#define DEFINE_FUNC_FH_IS(name, chip) \
unsigned int fh_is_##name(void) \
{ \
	struct fh_chip_info *info = fh_get_chip_info(); \
 \
	if (info) \
		return info->chip_id == FH_CHIP_##chip; \
	return 0; \
}

DEFINE_FUNC_FH_IS(8852, FH8852);
DEFINE_FUNC_FH_IS(8856, FH8856);
DEFINE_FUNC_FH_IS(8626v100, FH8626V100);
DEFINE_FUNC_FH_IS(8852v200, FH8852V200);
DEFINE_FUNC_FH_IS(8856v200, FH8856V200);
DEFINE_FUNC_FH_IS(8858v200, FH8858V200);
DEFINE_FUNC_FH_IS(8652, FH8652);
DEFINE_FUNC_FH_IS(8656, FH8656);
DEFINE_FUNC_FH_IS(8658, FH8658);
DEFINE_FUNC_FH_IS(8898, FH8898);
DEFINE_FUNC_FH_IS(8896, FH8896);
DEFINE_FUNC_FH_IS(8210, FH8210);
DEFINE_FUNC_FH_IS(8897, FH8897);
DEFINE_FUNC_FH_IS(8856v300, FH8856V300);
DEFINE_FUNC_FH_IS(8856v310, FH8856V310);
DEFINE_FUNC_FH_IS(8852v310, FH8852V310);
DEFINE_FUNC_FH_IS(8858v300, FH8858V300);
DEFINE_FUNC_FH_IS(8858v310, FH8858V310);
DEFINE_FUNC_FH_IS(8862, FH8862);
DEFINE_FUNC_FH_IS(8866, FH8866);
DEFINE_FUNC_FH_IS(6322, MC6322);
DEFINE_FUNC_FH_IS(6326, MC6326);
DEFINE_FUNC_FH_IS(6322L, MC6322L);
DEFINE_FUNC_FH_IS(6326L, MC6326L);
DEFINE_FUNC_FH_IS(6321, MC6321);
DEFINE_FUNC_FH_IS(6321L, MC6321L);
DEFINE_FUNC_FH_IS(3303, MC3303);


static int do_chip_info (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	struct fh_chip_info *info = fh_get_chip_info();

	if (info == NULL)
	{
		printf("unknown chip\n");
		return -1;
	}

	printf("name:\t\t%s\n", info->chip_name);
	printf("plat_id:\t0x%08x\n", info->_plat_id);
	printf("chip_id:\t0x%08x\n", info->chip_id);
	printf("ddrsize:\t%d Mbit\n", info->ddr_size);
	return 0;
}

U_BOOT_CMD(
	chipinfo,	1,	1,	do_chip_info,
	"get fh_chip_info",
	""
);
